using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Pricom
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
      tabControl1.Selected += new TabControlEventHandler(OnTabSelected);
      tabControl1.MouseDown += new MouseEventHandler(OnTabMiddleClick);
    }

    private void OnTabSelected(object sender, TabControlEventArgs e)
    {
      if (e.TabPageIndex < 0 || e.TabPageIndex > tabControl1.TabPages.Count)
        return;
      string url = tabControl1.TabPages[e.TabPageIndex].Tag.ToString();
      string channel = url.Substring(url.LastIndexOf('/') + 1);
      if (pricom.GetConnection(url) == null)
        return;
      focusSelectedTabInput = true;
      UpdateTopic(url, channel);
    }

    private void UpdateTopic(string url, string channel)
    {
      string topic = pricom.GetConnection(url).GetTopic(channel);
      this.Text = "Pricom - [" + channel + (topic.Length > 0 ? ": " : "") + topic + "]";
      GetPage(url).MarkTab(false);
      lastSelectedUrl = url;
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
      pricom.Shutdown();
      base.OnFormClosing(e);
    }

    // to script!
    private void closeToolStripMenuItem_Click(object sender, EventArgs e)
    {
      Close();
    }

    // class FlashWindow ?
    public void FlashWindowTick()
    {
      if (this.InvokeRequired)
        this.Invoke(new MethodInvoker(FlashWindowTick));
      else
      {
        WinAPI.FlashWindow(this.Handle, flashWindowState);
        WinAPI.FlashWindow(this.Handle, !flashWindowState);
        flashWindowState = !flashWindowState;
        if (notifyIcon1.Visible)
          notifyIcon1.Icon = flashWindowState ? notifyIcon2.Icon : ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));
      }
    }

    // class FlashWindow ?
    private void timer1_Tick(object sender, EventArgs e)
    {
      if (focusSelectedTabInput && tabControl1.SelectedTab != null)
      {
        GetPage(tabControl1.SelectedTab.Tag.ToString()).FocusInput();
        focusSelectedTabInput = false;
      }
      flashingWindow = flashingWindow && !this.ContainsFocus;
      if (flashingWindow)
        FlashWindowTick();
    }

    private void newConnectionToolStripMenuItem_Click(object sender, EventArgs e)
    {
      newConnection.ShowDialog();
      string proxy = newConnection.GetProxy();
      string server = newConnection.GetServer();
      string channel = newConnection.GetChannel();
      pricom.Connect(proxy, server, channel, newConnection.GetPort().ToString(), newConnection.GetNick());
    }

    void OnConnectionChanged(object sender, string url, string newUrl)
    {
      if (this.InvokeRequired)
        this.Invoke(new System.Windows.Forms.MethodInvoker(delegate() { OnConnectionChanged(sender, url, newUrl); }));
      else
      {
        if (!pages.ContainsKey(url))
          return;
        ChatPage page = pages[url];
        string channel = newUrl.Substring(newUrl.LastIndexOf('/') + 1);
        page.TabPage.Tag = newUrl;
        page.TabPage.Text = channel;
        page.TabPage.ToolTipText = newUrl;
        pages.Remove(url);
        pages.Add(newUrl, page);
      }
    }

    // handling event: join, part, nick
    void OnUserlistChanged(object sender, string channel, string url, string[] newUserlist)
    {
      ChatPage chatPage = GetPage(url);
      if (chatPage == null)
        return;

      if (newUserlist == null || newUserlist.Length == 0)
      {
        //todo: make option
        //if (chatPage.InputBox.InvokeRequired)
        //  chatPage.InputBox.BeginInvoke(new MethodInvoker(delegate() { chatPage.InputBox.Enabled = false; }));
        //else
        //  chatPage.InputBox.Enabled = false;

        CloseTab(url);
        return;
      }
      else
      {
        //if (chatPage.InputBox.InvokeRequired)
        //  chatPage.InputBox.BeginInvoke(new MethodInvoker(delegate() { chatPage.InputBox.Enabled = true; }));
        //else
        //  chatPage.InputBox.Enabled = true;
      }

      chatPage.ClearNicknames();
      foreach (string username in newUserlist)
        if (username.Length > 0)
          chatPage.AddNickname(username);
    }

    public void OnConnecting(string proxy, string server, string channel, string port, string nick)
    {
      string url = nick + "@" + server + ":" + port + "/" + channel;
      pricom.CreateConnection(url);
      pricom.GetConnection(url).MessageReceived += new MessageReceivedEventHandler(OnMessageReceived);
      pricom.GetConnection(url).UserlistChanged += new UserlistChangedEventHandler(OnUserlistChanged);
      pricom.GetConnection(url).ActionMessageReceived += new MessageReceivedEventHandler(OnActionMessageReceived);
      pricom.GetConnection(url).ConnectionChanged += new ConnectionChangedEventHandler(OnConnectionChanged);
      pricom.GetConnection(url).TopicChanged += new TopicChangedEventHandler(OnTopicChanged);
      WriteOutput(url, channel, " * Connecting to " + server + ":" + port + " ...");
    }

    void OnTopicChanged(object sender, string channel, string url, string newTopic, string changedBy)
    {
      if (tabControl1.InvokeRequired)
        tabControl1.BeginInvoke(new System.Windows.Forms.MethodInvoker(delegate() { OnTopicChanged(sender, channel, url, newTopic, changedBy); }));
      else
      {
        if (url != tabControl1.SelectedTab.Tag.ToString() || pricom.GetConnection(url) == null)
          return;
        if (changedBy.Length == 0)
        {
          Thread.Sleep(10);
          string topic = pricom.GetConnection(url).GetTopic(channel);
          if (topic.Length > 0)
            WriteOutput(url, channel, " * Topic is '" + topic + "'");
        }
        else
          WriteOutput(url, channel, "* " + changedBy + " changes topic to '" + newTopic + "'");
        UpdateTopic(url, channel);
      }
    }

    void OnActionMessageReceived(object sender, string from, string url, string message)
    {
      WriteOutput(url, from, " * " + message);
    }

    void OnMessageReceived(object sender, string from, string url, string message)
    {
      // utf8
      byte[] bytes = new byte[message.Length];
      for (int i = 0; i < message.Length; ++i)
        if ( Config.main.messages.fixUTF8bug152 )
          bytes[i] = i > 0 && (byte)message[i - 1] == (byte)0xc3 && (byte)message[i] == (byte)0x52 ? (byte)0xbc : (byte)message[i];
        else
          bytes[i] = (byte)message[i];
      byte[] bytesU_ = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, bytes);
      //using (System.IO.BinaryWriter w = new System.IO.BinaryWriter(new System.IO.FileStream(@"c:\_log.txt", System.IO.FileMode.Create))) { w.Write(bytes); }
      UTF8Encoding unicode = new UTF8Encoding(false, true);
      try
      {
        message = unicode.GetString(bytes);
      }
      catch
      {
      }

      WriteOutput(url, from, message);
      if (lastSelectedUrl != string.Empty && url != lastSelectedUrl)
        GetPage(url).MarkTab(true);
      flashingWindow = !this.ContainsFocus;
    }

    protected void OnStartQueryMessage(string nickName, string url)
    {
      if (nickName.StartsWith("@"))
        nickName = nickName.Remove(0, 1);
      ChatPage chatPage = GetPage(url);
      SelectTabPage(chatPage.TabPage);
    }

    public void WriteOutput(string url, string channel, string message)
    {
      if (InvokeRequired)
        BeginInvoke(new System.Windows.Forms.MethodInvoker(delegate() { WriteOutput(url, channel, message); }));
      else
      {
        ChatPage page = GetPage(url);
        page.WriteOutput(message);
        // SelectTabPage(GetPage(url).TabPage); 
      }
    }

    private void SelectTabPage(TabPage tabPage)
    {
      if (tabControl1.InvokeRequired)
        tabControl1.BeginInvoke(new System.Windows.Forms.MethodInvoker(delegate() { SelectTabPage(tabPage); }));
      else
        tabControl1.SelectTab(tabPage);
    }

    public void AddTabPage(TabPage tabPage)
    {
      if (tabControl1.InvokeRequired)
        tabControl1.BeginInvoke(new System.Windows.Forms.MethodInvoker(delegate() { AddTabPage(tabPage); }));
      else
        tabControl1.TabPages.Add(tabPage);
    }
    
    private ChatPage AddChatPage(string url)
    {
      string channel = url.Substring(url.LastIndexOf('/') + 1);
      TabPage tabPage = new TabPage();
      tabPage.Location = new System.Drawing.Point(4, 23);
      tabPage.Name = "tabPage1";
      tabPage.Padding = new System.Windows.Forms.Padding(3);
      tabPage.Size = new System.Drawing.Size(520, 307);
      tabPage.TabIndex = 0;
      tabPage.Text = (channel.Contains("#") ? "connecting..." : channel);
      tabPage.UseVisualStyleBackColor = true;
      tabPage.Tag = url;
      tabPage.ToolTipText = url;

      ChatPage chatPage = new ChatPage(tabPage, pricom.GetConnection(url));
      chatPage.Dock = DockStyle.Fill;
      chatPage.StartQueryMessage += new ChatPage.StartQueryMessageEventHandler(OnStartQueryMessage);
      chatPage.InputBox.EscapePressed += new Controls.HistoryTextBox.EscapePressedEventHandler(OnInputBoxEscapePressed);
      if ( !url.Contains("#") )
        chatPage.HideNickPanel();
      pages.Add(url, chatPage);

      tabPage.Controls.Add(chatPage);
      AddTabPage(tabPage);
      return chatPage;
    }

    void OnInputBoxEscapePressed(object sender)
    {
      Minimize();
    }

    void OnTabMiddleClick(object sender, MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Middle)
        return;
      for (int i = 0; i < tabControl1.TabCount; i++)
        if (tabControl1.GetTabRect(i).Contains(e.Location))
          CloseTab(tabControl1.TabPages[i].Tag.ToString());
    }

    void CloseTab(string url)
    {
      ChatPage chatPage = pages[url] as ChatPage;
      if (url.Contains("#"))
      {
        string channel = url.Substring(url.LastIndexOf('/') + 1);
        pricom.GetConnection(url).Leave(channel);
        pricom.RemoveConnection(url);
      }

      if (tabControl1.InvokeRequired)
        tabControl1.BeginInvoke((MethodInvoker)delegate() { tabControl1.TabPages.Remove(chatPage.TabPage); }, new object[] { chatPage.TabPage });
      else
        tabControl1.TabPages.Remove(chatPage.TabPage);

      pages.Remove(url);
    }

    private ChatPage GetPage(string url)
    {
      ChatPage chatPage;
      lock (pages)
      {
        if (pages.ContainsKey(url))
          chatPage = pages[url];
        else
          chatPage = AddChatPage(url);
      }
      return chatPage;
    }

    private void Minimize()
    {
      trayMinimizedWindow = true;
      notifyIcon1.Visible = true;
      Hide();
    }

    private void minimizeToolStripMenuItem_Click(object sender, EventArgs e)
    {
      Minimize();
    }

    void notifyIcon1_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      trayMinimizedWindow = true;
      notifyIcon1.Visible = false;
      this.Show();
      this.WindowState = FormWindowState.Normal;
      this.BringToFront();
    }

    private void Form1_Activated(object sender, EventArgs e)
    {
      flashingWindow = false;
      WinAPI.FlashWindow(this.Handle, false);
      WinAPI.FlashWindow(this.Handle, false);

      notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));
      if (tabControl1.SelectedTab != null && pages.ContainsKey(tabControl1.SelectedTab.Tag.ToString()))
      {
        string url = tabControl1.SelectedTab.Tag.ToString();
        pages[url].ScrollDown();
        pages[url].FocusInput();
      }
    }

    private void Form1_Layout(object sender, LayoutEventArgs e)
    {
      if (WindowState == FormWindowState.Minimized && !trayMinimizedWindow)
      {
        Minimize();
      }
      else if (WindowState != FormWindowState.Minimized)
        trayMinimizedWindow = false;
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
      pricom.OnConnecting += new Pricom.ConnectingEventHandler(OnConnecting);
      pricom.OnConnected += new Pricom.ConnectedEventHandler(OnConnected);
      pricom.LoadConfig();
    }

    private void OnConnected(string url)
    {
      //string channel = url.Substring(url.LastIndexOf('/') + 1);
      //ChatPage page = GetPage(channel, url);
      //page.ClearOutput();
    }

    private Pricom pricom = new Pricom();
    private NewConnectionForm newConnection = new NewConnectionForm();
    private Dictionary<string, ChatPage> pages = new Dictionary<string, ChatPage>(StringComparer.OrdinalIgnoreCase);
    private bool flashingWindow = false;
    private bool trayMinimizedWindow = false;
    private bool flashWindowState = true;
    private bool focusSelectedTabInput = false;
    private Crypto crypto = new Crypto();
    private string lastSelectedUrl = string.Empty;
  }
}